package com.ruoyi.workflow.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.workflow.domain.AppointmentInfo;
import com.ruoyi.workflow.domain.Statement;
import com.ruoyi.workflow.domain.StatementItem;
import com.ruoyi.workflow.domain.vo.StatementItemVo;
import com.ruoyi.workflow.mapper.StatementItemMapper;
import com.ruoyi.workflow.mapper.StatementMapper;
import com.ruoyi.workflow.service.IAppointmentInfoService;
import com.ruoyi.workflow.service.IStatementItemService;
import com.ruoyi.workflow.service.IStatementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.ruoyi.workflow.constant.AppointmentConstant.CMI_STATUS_PAYMENT;
import static com.ruoyi.workflow.constant.StatementConstant.APPOINTMENT_STATUS_NO;
import static com.ruoyi.workflow.constant.StatementConstant.STATEMENT_STATUS_PAID;

/**
 * StatementItemServiceImpl
 *
 * @author xiaodu
 * @date 2023/7/28
 */
@Service
public class StatementItemServiceImpl extends ServiceImpl<StatementItemMapper, StatementItem> implements IStatementItemService {

    @Autowired
    private StatementMapper statementMapper;
    @Autowired
    private IStatementService statementService;
    @Autowired
    private StatementItemMapper statementItemMapper;
    @Autowired
    private IAppointmentInfoService appointmentInfoService;

    /**
     * 获取结算单明细
     * 用于预约信息表的结算单： 根据预约表的id获得
     */
    @Override
    public Map<String, Object> getStatementItemByAppointmentInfoId(Long appointmentInfoId) {
        //1. 根据预约表的id获得 即 appointment_info_id 字段获取 结算单
        Statement statement = statementMapper.getStatementItemByAppointmentInfoId(appointmentInfoId);
        if (statement == null) {
            throw new ServiceException("appointmentInfoId 不存在");
        }
        //2. 获取服务项明细表
        Map<String, Object> resultMap = getStatementItem(statement);
        return resultMap;
    }

    /**
     * 获取结算单明细
     * 用于结算单信息表的明细单： 根据结算单表的id获得
     */
    @Override
    public Map<String, Object> getStatementItemByStatementId(Long statementId) {
        //1. 根据结算单表的id获得 即 bus_statement 的 id 字段获取 结算单
        Statement statement = statementMapper.selectOne(new LambdaQueryWrapper<Statement>()
                .eq(Statement::getId, statementId));
        if (statement == null) {
            throw new ServiceException("statementId 不存在");
        }
        //2.获取服务项明细表
        Map<String, Object> resultMap = getStatementItem(statement);
        return resultMap;
    }

    /**
     * 获取服务项明细表
     *
     * @param statement
     * @return
     */
    private Map<String, Object> getStatementItem(Statement statement) {
        //1. 根据结算单id，通过表bus_statement_item 结算单明细表，根据结算单id 即 statement_id 查询结算的服务
        List<StatementItem> statementItemList = statementItemMapper.selectList(new LambdaQueryWrapper<StatementItem>()
                .eq(StatementItem::getStatementId, statement.getId()));
        //2. 封装响应数据
        HashMap<String, Object> resultMap = new HashMap<>();
        resultMap.put("statementObj", statement);
        resultMap.put("statementItem", statementItemList);
        return resultMap;
    }

    /**
     * 保存在消费中结算单明细的服务项 【包含：新增，更新，删除】
     *
     * @param statementItemVo - Vo对象新增,更新,删除的列表
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveStatementItem(StatementItemVo statementItemVo) {
        //1.
        //考虑1：添加新明细
        //考虑2：明细数量变化
        //新增和数量变量 -> id区分新增和更新
        super.saveOrUpdateBatch(statementItemVo.getStatementItemList());
        //2.
        //考虑3：删除明细：记录删除的，并且是数据库存在的
        super.removeBatchByIds(statementItemVo.getDelStatementItemIds());
    }

    /**
     * 确认支付
     *
     * @param statementId 结算单的id
     * @param statementId 优惠价格
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void payStatementItem(Long statementId, BigDecimal preferentialPrice) {
        //0.结算单明细总消费金额和实付价格均需修改
        Statement statement = statementMapper.selectStatementById(statementId);
        //1.查询结算单明细
        List<StatementItem> statementItemList = statementItemMapper.selectList(new LambdaQueryWrapper<StatementItem>()
                .eq(StatementItem::getStatementId, statement.getId()));
        BigDecimal totalAmount = BigDecimal.ZERO;
        //2.计算总消费金额
        for (StatementItem statementItem : statementItemList) {
          totalAmount = totalAmount.add(statementItem.getItemPrice().multiply(new BigDecimal(statementItem.getItemQuantity())));
        }
        //3.计算实付价格
        BigDecimal discountAmount = totalAmount.subtract(preferentialPrice);
        //4.计算服务项明细数量
        int totalQuantity = statementItemList.size();

        //5.保存操作
        //5.0.保存总消费，实际支付
        //5.1.根据id把结算单的状态修改为  已支付
        //5.2.保存支付时间
        //5.3.保存支付人id
        statementService.update(new LambdaUpdateWrapper<Statement>()
                .set(Statement::getTotalAmount, totalAmount) //保存总消费价格
                .set(Statement::getDiscountAmount, discountAmount) //保存实际支付价
                .set(Statement::getTotalQuantity,totalQuantity) //保存服务项明细数量
                .set(Statement::getPayStatus, STATEMENT_STATUS_PAID) //修改状态为 已支付
                .set(Statement::getPayTime, new Date()) //保存支付时间
                .set(Statement::getPayeeId, SecurityUtils.getUserId()) //设置支付人的ID
                .eq(Statement::getId, statementId) //匹配结算单ID 即 statementId 进行更新操作
        );
        //6.预约信息状态修改
        //如果不是预约，则无需修改预约信息的状态
        if (APPOINTMENT_STATUS_NO.equals(statement.getAppointmentUserStatus())) {
            return;
        }
        //如果是预约的，则需要把预约信息中的状态修改为 已支付
        Long appointmentInfoId = statement.getAppointmentInfoId();
        appointmentInfoService.update(new LambdaUpdateWrapper<AppointmentInfo>()
                .set(AppointmentInfo::getStatus, CMI_STATUS_PAYMENT)
                .eq(AppointmentInfo::getId, appointmentInfoId));
    }
}


